踩坑之路:Activity执行finish以后onStop方法10s以后才调用

您所在的位置:网站首页 activity安卓 finish后重新打开 踩坑之路:Activity执行finish以后onStop方法10s以后才调用

踩坑之路:Activity执行finish以后onStop方法10s以后才调用

2024-07-10 18:24| 来源: 网络整理| 查看: 265

CDF_cc7d的博客地址:

https://www.jianshu.com/u/dd07890ebff0

正文

某天,测试提了一个bug,说当前页面关闭了以后回到了上一个页面,但是对应的音乐并没有立刻停止,而是过了一段时间才停止。于是翻阅了一下代码:

     @Override     protected void onStop() {         super.onStop();         if (mIsLoading) {             mAudioTool.pausePlayAudio();         }     }

mAudioTool.pausePlayAudio方法是停止播放音频的代码,似乎并没有什么问题。而且这段代码已经运行了好久,之前测试也是通过的,为何这个版本会出现这个问题?

所以首先我debug一下当前页面的onStop方法,结果发现页面关闭的时候onStop方法并没有被执行,然后过了差不多10s左右再被执行,感觉又是踩到了某个坑里面去了,仔细回想了最近改了的代码,问题可能出在了上一个页面的下面这段代码:

    /**      * 开始抖动      */     private fun startShake() {         if(mRotateAnim == null){             mRotateAnim = RotateAnimation(-2f, 2f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)         }         if (mRotateAnim!!.hasStarted() && !mRotateAnim!!.hasEnded()) {             //当前动画已经开始并且没有结束             return         }         //从左向右         mRotateAnim!!.duration = 50         mRotateAnim!!.repeatMode = Animation.REVERSE         mRotateAnim!!.repeatCount = Animation.INFINITE         val smallAnimationSet = AnimationSet(false)         smallAnimationSet.addAnimation(mRotateAnim)         cl_wrong_book.startAnimation(smallAnimationSet)     }

由于设置的repeatCount是INFINITE,所以动画是一直在执行中的。

不过还是有点纳闷,动画跟Activity生命周期有啥关系?为了印证下到底是不是Animation影响的当前页面生命周期调用异常,于是将这段代码进行了删除操作。结果Activity的onStop生命周期还真的就正常执行了。然后自己又做了一个demo,来查看下onStop的执行时间。

(这段是没有动画的)

2020-12-14 12:40:17.334 24575-24575/com.example.demo I/MainActivity: onCreate 2020-12-14 12:40:17.663 24575-24575/com.example.demo I/MainActivity: onStart 2020-12-14 12:40:17.670 24575-24575/com.example.demo I/MainActivity: onResume 2020-12-14 12:40:20.818 24575-24575/com.example.demo I/MainActivity: onPause 2020-12-14 12:40:20.836 24575-24575/com.example.demo I/SecondActivity: onCreate 2020-12-14 12:40:20.857 24575-24575/com.example.demo I/SecondActivity: onStart 2020-12-14 12:40:20.858 24575-24575/com.example.demo I/SecondActivity: onResume 2020-12-14 12:40:21.406 24575-24575/com.example.demo I/MainActivity: onStop 2020-12-14 12:40:22.930 24575-24575/com.example.demo I/SecondActivity: onPause 2020-12-14 12:40:22.936 24575-24575/com.example.demo I/MainActivity: onStart 2020-12-14 12:40:22.937 24575-24575/com.example.demo I/MainActivity: onResume 2020-12-14 12:40:23.439 24575-24575/com.example.demo I/SecondActivity: onStop 2020-12-14 12:40:23.440 24575-24575/com.example.demo I/SecondActivity: onDestroy

(这段是加上动画的)

2020-12-14 12:38:06.392 24278-24278/com.example.demo I/MainActivity: onCreate 2020-12-14 12:38:06.563 24278-24278/com.example.demo I/MainActivity: onStart 2020-12-14 12:38:06.565 24278-24278/com.example.demo I/MainActivity: onResume 2020-12-14 12:38:23.940 24278-24278/com.example.demo I/MainActivity: onPause 2020-12-14 12:38:23.964 24278-24278/com.example.demo I/SecondActivity: onCreate 2020-12-14 12:38:23.980 24278-24278/com.example.demo I/SecondActivity: onStart 2020-12-14 12:38:23.980 24278-24278/com.example.demo I/SecondActivity: onResume 2020-12-14 12:38:24.544 24278-24278/com.example.demo I/MainActivity: onStop 2020-12-14 12:38:28.111 24278-24278/com.example.demo I/SecondActivity: onPause 2020-12-14 12:38:28.117 24278-24278/com.example.demo I/MainActivity: onStart 2020-12-14 12:38:28.118 24278-24278/com.example.demo I/MainActivity: onResume 2020-12-14 12:38:38.153 24278-24278/com.example.demo I/SecondActivity: onStop 2020-12-14 12:38:38.155 24278-24278/com.example.demo I/SecondActivity: onDestroy

demo的log也显示了在加上动画以后,确实onStop和onDestroy就会被延迟执行,而且试了多次,发现每次延迟的时间都是10s左右。所以猜测一定存在某种定时执行onStop操作的场景,不然不可能每次都这么凑巧。

既然踩到坑,那么我就得想办法搞清楚为什么会出现这种情况。所以还是跟以前一样,查阅Android源码一探究竟。

finish()操作

    /**      * Finishes the current activity and specifies whether to remove the task associated with this      * activity.      */     @UnsupportedAppUsage     private void finish(int finishTask) {         if (mParent == null) {             int resultCode;             Intent resultData;             synchronized (this) {                 resultCode = mResultCode;                 resultData = mResultData;             }             if (false) Log.v(TAG, "Finishing self: token=" + mToken);             try {                 if (resultData != null) {                     resultData.prepareToLeaveProcess(this);                 }                 if (ActivityTaskManager.getService()                         .finishActivity(mToken, resultCode, resultData, finishTask)) {                     mFinished = true;                 }             } catch (RemoteException e) {                 // Empty             }         } else {             mParent.finishFromChild(this);         }         // Activity was launched when user tapped a link in the Autofill Save UI - Save UI must         // be restored now.         if (mIntent != null && mIntent.hasExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN)) {             getAutofillManager().onPendingSaveUi(AutofillManager.PENDING_UI_OPERATION_RESTORE,                     mIntent.getIBinderExtra(AutofillManager.EXTRA_RESTORE_SESSION_TOKEN));         }     }     /**      * Call this when your activity is done and should be closed.  The      * ActivityResult is propagated back to whoever launched you via      * onActivityResult().      */     public void finish() {         finish(DONT_FINISH_TASK_WITH_ACTIVITY);     }

Activity的finish方法会调用自身带有参数的finish方法,然后通过Binder会执行ActivityTaskManagerService的finishActivity方法。

    @Override     public final boolean finishActivity(IBinder token, int resultCode, Intent resultData,             int finishTask) {         ...代码省略...         synchronized (mGlobalLock) {             ...代码省略...             try {                 boolean res;                 final boolean finishWithRootActivity =                         finishTask == Activity.FINISH_TASK_WITH_ROOT_ACTIVITY;                 if (finishTask == Activity.FINISH_TASK_WITH_ACTIVITY                         || (finishWithRootActivity && r == rootR)) {                     ...代码省略                 } else {                     res = tr.getStack().requestFinishActivityLocked(token, resultCode,                             resultData, "app-request", true);                     if (!res) {                         Slog.i(TAG, "Failed to finish by app-request");                     }                 }                 return res;             } finally {                 Binder.restoreCallingIdentity(origId);             }         }     }

由于finishTask是DONT_FINISH_TASK_WITH_ACTIVITY类型,所以会走else分支,tr.getStack()得到的是ActivityStack对象,所以接下来执行的就是ActivityStack的requestFinishActivityLocked方法。

       /**      * @return Returns true if this activity has been removed from the history      * list, or false if it is still in the list and will be removed later.      */     final boolean finishActivityLocked(ActivityRecord r, int resultCode, Intent resultData,             String reason, boolean oomAdj, boolean pauseImmediately) {         if (r.finishing) {             //这个判断条件是为了防止多次进入,做了一道屏障             Slog.w(TAG, "Duplicate finish request for " + r);             return false;         }         mWindowManager.deferSurfaceLayout();         try {             //这个方法是为了将finishing设置为true             r.makeFinishingLocked();             final TaskRecord task = r.getTaskRecord();             EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY,                     r.mUserId, System.identityHashCode(r),                     task.taskId, r.shortComponentName, reason);             final ArrayList activities = task.mActivities;             final int index = activities.indexOf(r);             if (index ViewRootImpl#setView. If we are instead reusing                 // the decor view we have to notify the view root that the                 // callbacks may have changed.                 ViewRootImpl impl = decor.getViewRootImpl();                 if (impl != null) {                     impl.notifyChildRebuilt();                 }             }             if (a.mVisibleFromClient) {                 if (!a.mWindowAdded) {                     a.mWindowAdded = true;                     wm.addView(decor, l);                 } else {                     // The activity will get a callback for this {@link LayoutParams} change                     // earlier. However, at that time the decor will not be set (this is set                     // in this method), so no action will be taken. This call ensures the                     // callback occurs with the decor set.                     a.onWindowAttributesChanged(l);                 }             }             // If the window has already been added, but during resume             // we started another activity, then don't yet make the             // window visible.         } else if (!willBeVisible) {             if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");             r.hideForNow = true;         }         ...代码省略...         r.nextIdle = mNewActivities;         mNewActivities = r;         if (localLOGV) Slog.v(TAG, "Scheduling idle handler for " + r);         Looper.myQueue().addIdleHandler(new Idler());     }

handleResumeActivity的过程自然也不是本文的重点,重点关注最后一句话Looper.myQueue().addIdleHandler(new Idler()),这句话什么作用呢?这里就要重点看下addIdleHandler方法到底添加了什么东西。

MessageQueue.java

    public void addIdleHandler(@NonNull IdleHandler handler) {         if (handler == null) {             throw new NullPointerException("Can't add a null IdleHandler");         }         synchronized (this) {             mIdleHandlers.add(handler);         }     }

很简单的代码,只是将IdleHandler添加到mIdleHandlers的列表中,那么什么时候会使用这个对象呢。查看了mIdleHandlers的调用地方。

发现除了添加和删除IdleHandler对象以外只有MessageQueue的next方法里面有调用的地方。

   Message next() {         // Return here if the message loop has already quit and been disposed.         // This can happen if the application tries to restart a looper after quit         // which is not supported.         final long ptr = mPtr;         if (ptr == 0) {             return null;         }         int pendingIdleHandlerCount = -1; // -1 only during first iteration         int nextPollTimeoutMillis = 0;         for (;;) {             if (nextPollTimeoutMillis != 0) {                 Binder.flushPendingCommands();             }             nativePollOnce(ptr, nextPollTimeoutMillis);             synchronized (this) {                 // 获取当前时间                 final long now = SystemClock.uptimeMillis();                 Message prevMsg = null;                 Message msg = mMessages;                 if (msg != null && msg.target == null) {                     //target为空的情况下,才会进入此条件                     // Stalled by a barrier.  Find the next asynchronous message in the queue.                     do {                         prevMsg = msg;                         msg = msg.next;                         //遍历messageQueue里面的所有消息,如果存在message是异步的,那么返回给调用者调用。如果不存在异步消息,那么等遍历完退出循环                     } while (msg != null && !msg.isAsynchronous());                 }                 if (msg != null) {                     if (now 


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3